﻿using System;
using System.IO;
using System.Threading;
using Sync.Log;

    public class FileChecker
    {
        private FileSystemWatcher _firstDirectoryWatcher;
        private FileSystemWatcher _firstFileWatcher;
        private FileSystemWatcher _secondDirectoryWatcher;
        private FileSystemWatcher _secondFileWatcher;

        /// <summary>
        /// Sets up the Watchers
        /// </summary>
        /// <param name="firstWatcher">First Directory to Watch</param>
        /// <param name="MinuteWatcher">Minute Directory to Watch</param>
        public FileChecker(string firstWatcher, string MinuteWatcher)
        {
            //setup the watchers
            ChangeDirectories(firstWatcher, true);
            ChangeDirectories(MinuteWatcher, false);

            ////changes the filters
            //AddFilters();

            ////adds the subscribers
            //Subscribe(true);
            //Subscribe(false);
        }

        private void AddFilters(bool isFirstDirectoryChanging)
        {
            if (isFirstDirectoryChanging)
            {
                //directory watchers
                _firstDirectoryWatcher.LookAt(NotifyFilters.DirectoryName & NotifyFilters.LastWrite | NotifyFilters.DirectoryName);
                _firstFileWatcher.LookAt(NotifyFilters.FileName & NotifyFilters.LastWrite | NotifyFilters.FileName);
            }
            else
            {
                _secondDirectoryWatcher.LookAt(NotifyFilters.DirectoryName & NotifyFilters.LastWrite | NotifyFilters.DirectoryName);
                _secondFileWatcher.LookAt(NotifyFilters.FileName & NotifyFilters.LastWrite | NotifyFilters.FileName);
            }
        }

        /// <summary>
        /// Changes the Directory that a single Watcher looks at
        /// </summary>
        /// <param name="directory">the new directory to look at</param>
        /// <param name="isFirstDirectoryChanging">is the path changing for the first Directory Path</param>
        public void ChangeDirectories(string directory, bool isFirstDirectoryChanging)
        {
            try
            {
                DisposeWatchers(isFirstDirectoryChanging);
                if (isFirstDirectoryChanging)
                {
                    _firstDirectoryWatcher = new FileSystemWatcher(directory);
                    _firstFileWatcher = new FileSystemWatcher(directory);
                }
                else
                {
                    _secondDirectoryWatcher = new FileSystemWatcher(directory);
                    _secondFileWatcher = new FileSystemWatcher(directory);
                }
                AddFilters(isFirstDirectoryChanging);
                Subscribe(isFirstDirectoryChanging);
            }
            catch (Exception e)
            {
                SendMessage("An Error Was Made");
            }
        }

        /// <summary>
        /// Adds Subscribers to the Watchers
        /// </summary>
        /// <param name="isFirstDirectory">is the first directory being changed</param>
        private void Subscribe(bool isFirstDirectory)
        {
            //adds the subscribers
            if (isFirstDirectory)
            {
                AddSubscribers(_firstDirectoryWatcher, FileRenamer, FileFixer);
                AddSubscribers(_firstFileWatcher, FileRenamer, FileFixer);
            }
            else
            {
                AddSubscribers(_secondDirectoryWatcher, FileRenamer, FileFixer);
                AddSubscribers(_secondFileWatcher, FileRenamer, FileFixer);
            }
        }

        /// <summary>
        /// adds the methods that are listening
        /// </summary>
        /// <param name="thingToSubscribeTo"> watcher to subscribe to</param>
        /// <param name="renamerMethod">for Renamed events</param>
        /// <param name="syncMethod"> for other events</param>
        private void AddSubscribers(FileSystemWatcher thingToSubscribeTo, RenamedEventHandler renamerMethod,
                                    FileSystemEventHandler syncMethod)
        {
            thingToSubscribeTo.EnableRaisingEvents = true;
            thingToSubscribeTo.IncludeSubdirectories = true;
            thingToSubscribeTo.Renamed += renamerMethod;
            thingToSubscribeTo.Changed += syncMethod;
            thingToSubscribeTo.Created += syncMethod;
            thingToSubscribeTo.Deleted += syncMethod;
        }
        /// <summary>
        /// Disposes the watchers to be changed again
        /// </summary>
        /// <param name="isFirstDirectoryChanging"></param>
        private void DisposeWatchers(bool isFirstDirectoryChanging)
        {
            if (_firstDirectoryWatcher.IsNull() || _firstFileWatcher.IsNull() || _secondDirectoryWatcher.IsNull() ||
                _secondFileWatcher.IsNull()) return;
            if (isFirstDirectoryChanging)
            {
                _firstDirectoryWatcher.ReleaseResources();
                _firstFileWatcher.ReleaseResources();
            }
            else
            {
                _secondDirectoryWatcher.ReleaseResources();
                _secondDirectoryWatcher.ReleaseResources();
            }
        }

        private void DummyMethod(object sender, FileSystemEventArgs e)
        {
        }


        /// <summary>
        /// Sends a message out of the class
        /// </summary>
        /// <param name="message">message to send out of the class</param>
        private void SendMessage(string message, LogType type = LogType.Info)
        {
            Console.WriteLine(message);
        }

        /// <summary>
        /// This is called when a File or directory is renamed in either directory one or directory two
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void FileRenamer(object sender, RenamedEventArgs e)
        {
            var firstFileWatcher3 = (sender as FileSystemWatcher);
            if (firstFileWatcher3 != null)
            {
                //this is for the files that take longer to create
                //because they are inaccessable while being created
                //e.x. Microsoft Access Database
                Thread.Sleep(80);
                string pathFrom = GetOldPath(firstFileWatcher3, e);
                string pathToGo = GetPath(firstFileWatcher3, e);
                bool IsDirectory = false;
                char stuff = pathFrom.ToCharArray()[pathFrom.ToCharArray().Length - 1];
                if (stuff.Equals('\\'))
                {
                    IsDirectory = true;
                }
                if (IsDirectory)// && firstFileWatcher3.NotifyFilter == (NotifyFilters.DirectoryName & NotifyFilters.LastWrite))
                {
                    var info = new DirectoryInfo(e.FullPath);
                    //if the directory was not just written to
                    if (!info.LastWriteTime.Minute.Equals(
                        new DirectoryInfo(pathToGo).LastWriteTime.Minute))
                    {
                        SendMessage("Renamed Directory: From: " + pathFrom + " To: " + pathToGo);
                        Directory.Move(pathFrom, pathToGo);
                        if (Directory.Exists(pathFrom))
                        {
                            Directory.Delete(pathFrom, true);
                        }
                    }
                }
                else// if(firstFileWatcher3.NotifyFilter == (NotifyFilters.FileName | NotifyFilters.LastWrite))
                {
                    if (System.IO.File.Exists(pathFrom))
                    {
                        var file = new FileInfo(e.FullPath);
                        if (
                            !file.LastWriteTime.Minute.Equals(
                                new FileInfo(pathToGo).LastWriteTime.Minute))
                        {
                            SendMessage("Renamed File: From: " + pathFrom + " To: " + pathToGo);
                            System.IO.File.Move(pathFrom, pathToGo);
                        }
                    }
                }
            }
        }
        /// <summary>
        /// This method is called when things in directory one 
        /// or directory two are either changed, deleted, or created
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void FileFixer(object sender, FileSystemEventArgs e)
        {
            try
            {
                var firstFileWatcher3 = (sender as FileSystemWatcher);
                if (firstFileWatcher3 != null)
                {
                    //this is for extensions .accdb and .as
                    Thread.Sleep(80);
                    string pathToGo = GetPath(firstFileWatcher3, e);
                    bool IsDirectory = false;
                    char stuff = pathToGo.ToCharArray()[pathToGo.ToCharArray().Length - 1];
                    if (stuff.Equals('\\'))
                    {
                        IsDirectory = true;
                    }
                    //if the filewatcher that called this is a Directory watcher
                    if (IsDirectory) //firstFileWatcher3.NotifyFilter == (NotifyFilters.DirectoryName & NotifyFilters.LastWrite))//firstFileWatcher3 == _firstDirectoryWatcher || firstFileWatcher3 == _secondDirectoryWatcher)
                    {
                        var info = new DirectoryInfo(e.FullPath);
                        switch (e.ChangeType)
                        {
                            case WatcherChangeTypes.Created:

                            case WatcherChangeTypes.Changed:
                                Console.ForegroundColor = ConsoleColor.Yellow;
                                //if the directory exists where it is going
                                if (Directory.Exists(pathToGo))
                                {
                                    //if the file was not just edited
                                    if (!info.LastWriteTime.Minute.Equals(
                                        new DirectoryInfo(pathToGo).LastWriteTime.Minute))
                                    {
                                        SendMessage("Copied Directory: " + pathToGo);
                                        CopyDirectory(info, new DirectoryInfo(pathToGo));
                                    }
                                }
                                else
                                {
                                    SendMessage("Copied Directory: " + pathToGo);
                                    CopyDirectory(info, new DirectoryInfo(pathToGo));
                                }
                                Console.ResetColor();
                                break;
                            case WatcherChangeTypes.Deleted:
                                Console.ForegroundColor = ConsoleColor.Red;
                                //if the directory exists
                                if (Directory.Exists(pathToGo))
                                {
                                    //delete it
                                    SendMessage("Deleted Directory: " + pathToGo);
                                    Directory.Delete(pathToGo, true);
                                }
                                Console.ResetColor();
                                break;
                        }
                    }
                    //if the filewatcher sent in is a File Watcher
                    else //if(!IsDirectory && firstFileWatcher3.NotifyFilter == (NotifyFilters.FileName | NotifyFilters.LastWrite))
                    {
                        var file = new FileInfo(e.FullPath);
                        switch (e.ChangeType)
                        {
                            case WatcherChangeTypes.Created:


                            case WatcherChangeTypes.Changed:
                                Console.ForegroundColor = ConsoleColor.Yellow;
                                if (System.IO.File.Exists(pathToGo))
                                {
                                    //if the file was not just written to
                                    if (
                                        !file.LastWriteTime.Minute.Equals(
                                            new FileInfo(pathToGo).LastWriteTime.Minute))
                                    {
                                        SendMessage("Copied File: " + pathToGo);
                                        file.CopyTo(pathToGo, true);
                                    }
                                }
                                else
                                {
                                    SendMessage("Copied File: " + pathToGo);
                                    //file.CopyTo(pathToGo, true);
                                }
                                Console.ResetColor();
                                break;
                            case WatcherChangeTypes.Deleted:
                                if (System.IO.File.Exists(pathToGo))
                                {
                                    //if the file was not just written to
                                    if (
                                        !file.LastWriteTime.Minute.Equals(
                                            new FileInfo(pathToGo).LastWriteTime.Minute))
                                    {
                                        Console.ForegroundColor = ConsoleColor.Red;
                                        SendMessage("Deleted File: " + pathToGo);
                                        System.IO.File.Delete(pathToGo);
                                        Console.ResetColor();
                                    }
                                }
                                break;
                        }
                    }
                }
                else
                {
                    SendMessage("Watcher be Null. This should never activate!", LogType.Error);
                }
            }
            catch (IOException e1)
            {
            }
        }
        /// <summary>
        /// gets the old path name for Renamed
        /// </summary>
        /// <param name="firstFileWatcher3"></param>
        /// <param name="e"></param>
        /// <returns></returns>
        private string GetOldPath(FileSystemWatcher firstFileWatcher3, RenamedEventArgs e)
        {
            string pathFrom;
            //if the watcher is watching files
            if (firstFileWatcher3 == _firstFileWatcher)
            {
                pathFrom = _secondFileWatcher.Path + "\\" + e.OldName;
            }
            else if (firstFileWatcher3 == _secondFileWatcher)
            {
                pathFrom = _firstFileWatcher.Path + "\\" + e.OldName;
            }
                //if the watcher is watching directories
            else if (firstFileWatcher3 == _firstDirectoryWatcher)
            {
                pathFrom = _secondDirectoryWatcher.Path + "\\" + e.OldName + "\\";
            }
            else
            {
                pathFrom = _firstDirectoryWatcher.Path + "\\" + e.OldName + "\\";
            }
            return pathFrom;
        }

        /// <summary>
        /// gets the new path name for the file or directory to head to
        /// </summary>
        /// <param name="firstFileWatcher3"></param>
        /// <param name="e"></param>
        /// <returns></returns>
        private string GetPath(FileSystemWatcher firstFileWatcher3, FileSystemEventArgs e)
        {
            string pathToGo;
            //if the watcher is watching Files
            if (firstFileWatcher3 == _firstFileWatcher)
            {
                pathToGo = _secondFileWatcher.Path + "\\" + e.Name;
            }
            else if (firstFileWatcher3 == _secondFileWatcher)
            {
                pathToGo = _firstFileWatcher.Path + "\\" + e.Name;
            }
                //if the watcher is watching Directories
            else if (firstFileWatcher3 == _firstDirectoryWatcher)
            {
                pathToGo = _secondDirectoryWatcher.Path + "\\" + e.Name + "\\";
            }
            else
            {
                pathToGo = _firstDirectoryWatcher.Path + "\\" + e.Name + "\\";
            }
            return pathToGo;
        }

        

        /// <summary>
        /// copies a specified Directory and Everything in it recursively
        /// </summary>
        /// <param name="placeThatChanged">place that changed</param>
        /// <param name="placeToChange">place to change</param>
        private void CopyDirectory(DirectoryInfo placeThatChanged, DirectoryInfo placeToChange)
        {
            if (placeThatChanged.FullName.ToLower() == placeToChange.FullName.ToLower())
            {
                return;
            }
            //if directory doesnt exist
            if (!Directory.Exists(placeToChange.FullName))
            {
                //create it
                SendMessage("Created Directory: " + placeToChange.FullName);
                Directory.CreateDirectory(placeToChange.FullName);
            }
            var files = placeThatChanged.GetFiles();
            //go through all files inside the directory
            foreach (FileInfo info in files)
            {
                try
                {
                    SendMessage("Copied File: " + placeToChange.FullName + "\\" + info.Name);
                    info.CopyTo(Path.Combine(placeToChange.ToString(), info.Name), true);
                }
                catch (Exception e)
                {
                }
            }
            var dirs = placeThatChanged.GetDirectories();
            //go through all subdirectories
            foreach (DirectoryInfo dirInfo in dirs)
            {
                try
                {
                    DirectoryInfo dirInfo2 = placeToChange.CreateSubdirectory(dirInfo.Name);
                    CopyDirectory(dirInfo, dirInfo2);
                }
                catch (Exception e)
                {
                }
            }
        }
    }